﻿//-----------------------------------------------------------------------
// <copyright file="CrmCommandLineUtilities.cs" company="Ryan Corrigal">
//     Ryan Corrigal. All rights reserved.
// </copyright>
//-----------------------------------------------------------------------
namespace CrmCommandLineUtilities
{
    using System;
    using System.Collections.Generic;
    using System.IO;
    using System.ServiceModel.Description;
    using Microsoft.Crm.Sdk.Messages;
    using Microsoft.Xrm.Sdk;
    using Microsoft.Xrm.Sdk.Client;
    using Microsoft.Xrm.Sdk.Query;

    /// <summary>
    /// CRM Command Line Utilities
    /// Original code by Ryan Corrigal
    /// </summary>
    public class CrmCommandLineUtilities
    {
        /// <summary>
        /// The main logic for this program. Parses command line arguments and processes as directed.
        /// </summary>
        /// <param name="args">The parameters that are to be captured by the program.</param>
        public static void Main(string[] args)
        {
            // Store the help text in this variable
            string helpText = "CRM Command Line Utilities \n \nUsage: \nrun this program from the command prompt. If you run into issues, you may have to 'Run as Administrator'. \n \nExample: \nCrmCommandLineUtilities.exe /o:http://crm2011online.com /u:crm\ryancorrigal /p:pass@word1 /f:MicrosoftXrmCustomerPortal.zip /c:i \n \nCommand Line Switches: \nManditory: \n  /O: - Defines the Organization to which the selected operation is to be performed \n  /U: - The active directory name of the user to who you would like to perform the operation as (domain\\username) \n  /P: - The password of the specified user name \n  /S: - The solution, if applicable to w\nch the operation will be performed on (note: this is the 'Name' and not 'Display Name') \n  /C: - the command you wish to perform \n        I  - performs an import operation \n        E  - performs an unmanaged solution export from the solution specified with the 'S' flag to the file specified with the 'F' \n \nflag \n        EM - performs a managed solution export from the solution specified with the 'S' flag to the file specified with the 'F' flag \n        U  - perfoms a solution uninstall to the solution specified with the 'S' flag \nConditional: \n  /F: - The file, if applicabale, to w\nch the operation will be performed to or from.";

            // Create some variables to store our captured command line flags in.
            string fileName = string.Empty;
            string solutionName = string.Empty;
            var organizationUrl = string.Empty;
            var credentials = new ClientCredentials();
            var command = string.Empty;

            if (args.Length.Equals(0))
            {
                throw new Exception("You must provide flags to use this utility.");
            }

            try
            {
                foreach (string arg in args)
                {
                    switch (arg.Substring(0, 3).ToLower())
                    {
                        case "/e:":
                            command = "e";
                            break;
                        case "/o:":
                            organizationUrl = arg.Substring(3);
                            break;
                        case "/u:":
                            credentials.UserName.UserName = arg.Substring(3);
                            break;
                        case "/p:":
                            credentials.UserName.Password = arg.Substring(3);
                            break;
                        case "/f:":
                            fileName = arg.Substring(3);
                            break;
                        case "/s:":
                            solutionName = arg.Substring(3);
                            break;
                        case "/c:":
                            command = arg.Substring(3).ToLower();
                            break;
                    }
                }

                // We check for some manditory flags and throw an exception to the user if they are not fufilled.
                VerifyManditoryValue(command, "You must specify a command that you wish to perform using the '/c:' flag.");
                VerifyManditoryValue(organizationUrl, "You must specify the organization with the '/o:' flag in order to perform an operation.");
                VerifyManditoryValue(credentials.UserName.UserName, "You must specify the user to which you wish to perform the operation as with the '/u:' flag.");
                VerifyManditoryValue(credentials.UserName.Password, "You must specify the password with the '/p:' flag for the user to which you wish to perform the operation as.");

                System.Console.WriteLine("Connecting to service");
                var organizationUri = new Uri(organizationUrl + "/XRMServices/2011/Organization.svc");
                var service = new OrganizationServiceProxy(organizationUri, null, credentials, null);

                System.Console.WriteLine("Connected");
                switch (command)
                {
                    case "i":
                        System.Console.WriteLine("Import solution command executing.");
                        SolutionImportWithMonitoring(service, solutionName, fileName, organizationUrl);
                        break;
                    case "e":
                        System.Console.WriteLine("Export unmanaged solution command executing.");
                        SolutionExport(service, solutionName, fileName, false);
                        break;
                    case "em":
                        System.Console.WriteLine("Export managed solution command executing.");
                        SolutionExport(service, solutionName, fileName, true);
                        break;
                    case "u":
                        System.Console.WriteLine("Uninstall solution command executing.");
                        SolutionUninstall(service, solutionName);
                        break;
                }
            }
            catch (Exception e)
            {
                System.Console.WriteLine("An error has occurred...");
                System.Console.WriteLine();
                System.Console.WriteLine("Error Message:");
                System.Console.WriteLine(e.Message);
                System.Console.WriteLine();
                System.Console.WriteLine(helpText);
            }
        }

        /// <summary>
        /// Verifies that a value is not null or blank and, if it is, throws an exception with the specified message.
        /// </summary>
        /// <param name="value">The string value to check.</param>
        /// <param name="exceptionMessage">The excpetion message to throw, if the value fails validation.</param>
        private static void VerifyManditoryValue(string value, string exceptionMessage)
        {
            if (string.IsNullOrEmpty(value))
            {
                throw new Exception(exceptionMessage);
            }
        }

        /// <summary>
        /// Exports the solution specified to the file specified from the organization specified.
        /// </summary>
        /// <param name="service">The OrganizationServiceProxy that will be used to execute the command.</param>
        /// <param name="solutionName">The name of the solution to be exported.</param>
        /// <param name="fileName">The name of the file to which the exported solution will be saved.</param>
        /// <param name="managed">Specifies whether the solution exported is managed or unmanaged.</param>
        private static void SolutionExport(OrganizationServiceProxy service, string solutionName, string fileName, bool managed)
        {
            ExportSolutionRequest exportSolutionRequest = new ExportSolutionRequest();
            exportSolutionRequest.SolutionName = solutionName;
            exportSolutionRequest.Managed = managed;

            ExportSolutionResponse exportSolutionResponse = (ExportSolutionResponse)service.Execute(exportSolutionRequest);

            byte[] exportXml = exportSolutionResponse.ExportSolutionFile;
            string filename = fileName;
            File.WriteAllBytes(filename, exportXml);

            Console.WriteLine("Solution exported to {0}.", filename);
        }

        /// <summary>
        /// Uninstalls the specified solution.
        /// </summary>
        /// <param name="service">The OrganizationServiceProxy that will be used to execute the command.</param>
        /// <param name="solutionName">The solution to be uninstalled.</param>
        private static void SolutionUninstall(OrganizationServiceProxy service, string solutionName)
        {
            QueryExpression queryImportedSolution = new QueryExpression
            {
                EntityName = "solution",
                ColumnSet = new ColumnSet(new string[] { "solutionid", "friendlyname" }),
                Criteria = new FilterExpression()
            };

            queryImportedSolution.Criteria.AddCondition("uniquename", ConditionOperator.Equal, solutionName);

            Entity importedSolution = service.RetrieveMultiple(queryImportedSolution).Entities[0];

            service.Delete("solution", importedSolution.Id);

            Console.WriteLine("Deleted the {0} solution.", solutionName);
        }

        /// <summary>
        /// Imports the solution specified from the file specified to the organization specified.
        /// </summary>
        /// <param name="service">The OrganizationServiceProxy that will be used to execute the command.</param>
        /// <param name="solutionName">The name of the solution imported (for display only, not necessary).</param>
        /// <param name="fileName">The file from which the solution will be imported from.</param>
        /// <param name="organizationUrl">The URL for the organization to which the solution will be imported to.</param>
        private static void SolutionImportWithMonitoring(OrganizationServiceProxy service, string solutionName, string fileName, string organizationUrl)
        {
            string managedSolutionLocation = fileName;
            byte[] fileBytes = File.ReadAllBytes(managedSolutionLocation);

            service.Timeout = new TimeSpan(0, 60, 0);

            byte[] fileBytesWithMonitoring = File.ReadAllBytes(managedSolutionLocation);

            ImportSolutionRequest importSolutionRequest = new ImportSolutionRequest()
            {
                CustomizationFile = fileBytes,
                ImportJobId = Guid.NewGuid()
            };

            service.Execute(importSolutionRequest);
            Console.WriteLine("Imported Solution with Monitoring from {0}", managedSolutionLocation);
            Entity job = service.Retrieve("importjob", importSolutionRequest.ImportJobId, new ColumnSet(new string[] { "data", "solutionname" }));

            System.Xml.XmlDocument doc = new System.Xml.XmlDocument();
            doc.LoadXml(job["data"].ToString());

            string importedSolutionName = doc.SelectSingleNode("//solutionManifest/UniqueName").InnerText;
            string solutionImportResult = doc.SelectSingleNode("//solutionManifest/result/@result").Value;

            Console.WriteLine("Report from the ImportJob data");
            Console.WriteLine("Solution Unique name: {0}", importedSolutionName);
            Console.WriteLine("Solution Import Result: {0}", solutionImportResult);
            Console.WriteLine(string.Empty);
        }
    }
}